package org.xx.sipahi.controllers.portal;

import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.TemplateException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.xx.armory.commons.SysUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Map;

import static org.xx.armory.commons.DebugUtils.dumpTraceBack;
import static org.xx.armory.commons.UrlUtils.joinUrl;

@Component
public class FreeMarkerRenderer
        implements InitializingBean {
    private static final String TEMPLATE_EXT = ".ftl";

    private final Logger logger = LoggerFactory.getLogger(FreeMarkerRenderer.class);

    private Configuration configuration;

    @Value("${vfs.root-path}/Templates")
    private File rootTemplatePath;

    @Value("${app.root-url:/}")
    private String rootUrl;

    @Value("${app.context-url:/}")
    private String contextUrl;

    private String portalUrl;

    public void render(
            String tpl,
            Map<String, Object> model,
            String encoding,
            HttpServletRequest httpServletRequest,
            HttpServletResponse httpServletResponse
    )
            throws IOException {
        if (!tpl.endsWith(TEMPLATE_EXT)) {
            tpl = tpl + TEMPLATE_EXT;
        }
        final var template = this.configuration.getTemplate(tpl, encoding);

        try {
            model.put("requestContext", new RequestContext(httpServletRequest, httpServletResponse));
            final var environment = template.createProcessingEnvironment(model, httpServletResponse.getWriter());
            environment.setOutputEncoding(encoding);
            environment.addAutoImport("macros", "/macros.ftl");
            environment.process();
        } catch (TemplateException ex) {
            logger.warn("Cannot render page with template\n{}", dumpTraceBack(ex));
        }
    }

    @Override
    public void afterPropertiesSet()
            throws Exception {
        this.configuration = new Configuration(Configuration.VERSION_2_3_30);
        if (this.rootTemplatePath.exists()) {
            if (!this.rootTemplatePath.isDirectory()) {
                throw new IllegalStateException("Path " + this.rootTemplatePath + " is not a directory");
            }
        } else {
            if (SysUtils.mkdirs(this.rootTemplatePath)) {
                logger.info("Template path " + this.rootTemplatePath + " created");
            } else {
                throw new IllegalStateException("Cannot make directory " + this.rootTemplatePath);
            }
        }
        this.configuration.setTemplateLoader(new FileTemplateLoader(this.rootTemplatePath));

        this.portalUrl = joinUrl(this.contextUrl, "/portal/");
    }

    public final class RequestContext {
        private final HttpServletRequest httpServletRequest;
        private final HttpServletResponse httpServletResponse;

        RequestContext(
                HttpServletRequest httpServletRequest,
                HttpServletResponse httpServletResponse
        ) {
            this.httpServletRequest = httpServletRequest;
            this.httpServletResponse = httpServletResponse;
        }

        public String relative(
                String absoluteUrl
        ) {
            absoluteUrl = absoluteUrl != null ? absoluteUrl.strip() : "/";
            final var endWithSlash = absoluteUrl.endsWith("/");
            if (absoluteUrl.startsWith("~/")) {
                absoluteUrl = joinUrl(FreeMarkerRenderer.this.portalUrl, absoluteUrl.substring(2));
            } else {
                absoluteUrl = joinUrl(FreeMarkerRenderer.this.rootUrl, absoluteUrl);
            }

            final var requestPath = httpServletRequest.getRequestURI();
            var resultUrl = Path.of(requestPath)
                                .getParent()
                                .relativize(Path.of(absoluteUrl))
                                .toString().replace('\\', '/');
            if (this.httpServletResponse != null) {
                resultUrl = this.httpServletResponse.encodeURL(resultUrl);
            }

            if (endWithSlash) {
                resultUrl = resultUrl + "/";
            }

            logger.debug("{}: {} ==> {}", requestPath, absoluteUrl, resultUrl);

            return resultUrl;
        }
    }
}
